package com.bigshen.algorithm.aLinearList.solution05LRUCache;

import java.util.HashMap;
import java.util.Map;

/**
 *
 *

 ### 146. LRU Cache

 Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

 Implement the LRUCache class:

 LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
 int get(int key) Return the value of the key if the key exists, otherwise return -1.
 void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

 Example 1:

 Input
 ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
 [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
 Output
 [null, null, null, 1, null, -1, null, -1, 3, 4]

 Explanation
 LRUCache lRUCache = new LRUCache(2);
 lRUCache.put(1, 1); // cache is {1=1}
 lRUCache.put(2, 2); // cache is {1=1, 2=2}
 lRUCache.get(1);    // return 1
 lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
 lRUCache.get(2);    // returns -1 (not found)
 lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
 lRUCache.get(1);    // return -1 (not found)
 lRUCache.get(3);    // return 3
 lRUCache.get(4);    // return 4

 来源：力扣（LeetCode）
 链接：https://leetcode-cn.com/problems/lru-cache
 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。

 *
 */
class LRUCache {

    class Node {
        int key;
        int value;
        Node prev;
        Node next;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    int capacity;
    private Node head = new Node(-1,-1);
    private Node tail = new Node(-1,-1);
    Map<Integer, Node> cache = new HashMap<>();

    public LRUCache(int capacity) {
        this.capacity = capacity;
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        if (!cache.containsKey(key)) {
            // 缓存不存在
            return -1;
        }
        // 提取节点，关系解绑
        Node exist = cache.get(key);
        exist.prev.next = exist.next;
        exist.next.prev = exist.prev;
        // 移至队尾
        moveToTail(exist);
        return exist.value;
    }

    public void put(int key, int value) {
        if (get(key) != -1) {
            // 已存在，复用get方法提取至队尾，重置value刷新缓存
            cache.get(key).value = value;
            return;
        }

        // 不存在，插入
        Node insertNode = new Node(key, value);
        if (cache.size() >= capacity) {
            // 队列已满
            // 移除头结点
            Node del = head.next;
            head.next = del.next;
            del.next.prev = head;
            del.prev = null;
            del.next = null;
            // 插入队尾
            moveToTail(insertNode);
            // 移除缓存
            cache.remove(del.key);
        } else {
            // 队列未满，直接插入队尾
            moveToTail(insertNode);
        }
        cache.put(key, insertNode);
    }

    private void moveToTail(Node node) {
        // 插入队尾
        node.prev = tail.prev;
        node.next = tail;
        tail.prev.next = node;
        tail.prev = node;
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
